package tool.dialog;

import inputstream.StringInputStream;
import inputstream.StringStream;

import java.awt.Color;

/**rappresenta una TextBox in cui l'utente puo' inserire un array di tipi.
 * Non tutti i tipi possono essere inseriti (anche se la classe lo consente
 * l'utilizzo improprio potrebbe causare comporetamente anomali), ma solo
 * quelli elencati in questa lista:
 * <ul>
 *  <li>String</li>
 *  <li>estensioni di Number</li>
 * </ul>
 * I vari oggetti vengono divisi nella stringa tramite la costante {@value #GAP};
 * la costante non e' di un solo carattere poich' puo' accadere che in una stringa
 * sia necessario scrivere proprio tale carattere. l'uso quindi del doppio
 * {@value #GAP} rende tale scenario molto improbabile.
 * <p>Un'altra cosa che potrebbe creare confusione e' la spaziatura tra {@value #GAP} e
 * la stringa rappresentante il tipo TYPE: la classe e' progettata in modo tale
 * che rilevi, senza problemi <strong>da 0 (incluso) a infiniti spazi</strong> 
 * tra le varie stringhe e {@value #GAP}. Quindi tutti questi esempio sono perfettamente leciti:
 * <p>45{@value #GAP}34
 * <p>45{@value #GAP} 34
 * <p>45 {@value #GAP}34
 * <p>45         {@value #GAP}     34
 * 
 * <p>per estendere questa classe occore implementare i metodi {@link #getArrayInstance(int)} e
 * {@link #readType(StringStream)}:
 * <ul>
 *  <li>{@link #getArrayInstance(int)} e' un metodo semplice da implementare; e' necessario
 *  creare un array vuoto della lunghezza passata e ritornarlo tramite return</li>
 *  <li>{@link #readType(StringStream)}: questo metodo viene richiamato piu' volte per
 *  leggere dallo stream del TextField i vari valori delle celle dell'array. Per implementarlo
 *  correttamente serve che all'inizio lo stream passato venga letto e che, in caso la
 *  stringa non sia convertibile, ritornare il valore NULL. una documentazione piu' approfondita
 *  viene dispiegata in tale metodo</li>
 * </ul>
 * 
 * @author Koldar
 * @version 1.0 
 *
 * @param <TYPE> indica il tipo di ogni cella dell'array
 */
public abstract class JArrayField<TYPE> extends JField<TYPE[]>{

	private static final long serialVersionUID = 7935614366560863189L;
	/**rappresenta la stringa che deve essere usata per dividere 2 valori diverse nella stringa rappresentante l'array*/
	public static final String GAP=",,";
	/**indica l'espressione regolare che divide 2 diversi valori nell'array*/
	private static final String REGEX_TYPEGAP="\\s*"+GAP+"\\s*";
	
	/**rappresenta l'espressione regolare che esprime il formato stringa del tipo TYPE*/
	private String typeExpression;
	private String matcher;
	
	public JArrayField(Class<TYPE[]> numberused,String _typeexpression){
		this("",CORRECT,numberused,_typeexpression);
	}
	
	public JArrayField(String startfield,int initialstate,Class<TYPE[]> numberused,String _typeexpression){
		this(startfield,initialstate,numberused,_typeexpression,COLOR_ERROR,COLOR_DEFAULT,COLOR_CORRECT);
	}
	/**costruisce un nuovo TextField migliorato per offrire servizi di conversione Stringa-Array
	 * 
	 * @param startfield indica la stringa da visualizzare all'inizio nel field
	 * @param initialstate indica lo stato iniziale che la textfield deve assumere (WRONG o CORRECT)
	 * @param numberused indica la classe rappresentante il tipo di dato con cui comporre l'array
	 * @param _typeexpression indica <strong>l'espressione regolare</strong> che rappresenta in modo generale qualunque valore del dato inserito nell'array
	 * @param error indica il colore da usare in caso in cui lo stato sia di errore
	 * @param standard indica il colore da usare in caso in cui lo stato sia "in modifica"
	 * @param correct indica il colore da usare in caso in cui lo stato sia corretto
	 */
	public JArrayField(String startfield, int initialstate,	Class<TYPE[]> numberused,String _typeexpression, Color error, Color standard, Color correct) {
		super(startfield, initialstate, numberused, error, standard, correct);
		this.typeExpression=_typeexpression;
		//o niente oppure un TYPE seguito eventualmente da altri TYPE separati da , (spazi quanti ne vuoi)
		this.matcher="$|\\s*"+this.typeExpression+"("+REGEX_TYPEGAP+this.typeExpression+")*";
	}
	
	/**data la stringa, conta il numero di virgole totali
	 * 
	 * @param str la stirnga da esaminare
	 * @return il numero di virgole totali nella stringa dividendole per 2 e sommandoci 1. In questo modo
	 * vengono contate le stringhe che sono divise dalle virgole
	 */
	private int countCommas(String str){
		int result=0;
		for(int i=0;i<str.length();i++){
			if (str.charAt(i)==','){
				result++;
			}
		}
		return (result/2)+1;
	}

	@Override
	public TYPE[] getInterestedValue() {
		if (this.getText().equals("")){//array vuoto
			return this.getArrayInstance(0);
		}
		this.value=this.getArrayInstance(this.countCommas(this.getText()));
		StringStream stream=new StringStream(new StringInputStream(this.getText()),REGEX_TYPEGAP);
		for (int i=0;i<this.value.length;i++){
			this.value[i]=this.readType(stream);
			if (this.value[i]==null){
				return null;
			}
		}
		return this.value;
	}
	
	/**quando la classe deve leggere i valori all'interno della stringa che rappresenta
	 * gli array non sa come puo' trasformare un sottpezzo di stringa in un valore
	 * di tipo TYPE. Questo metodo contiene la lista di istruzioni che permette
	 * di trasformare la stringa in valore di tipo TYPE.
	 * <p>Affinche' il tutto funzioni <strong>e' necessario per prima cosa leggere
	 * dallo stream una stringa</strong>, con uno dei metodi offerti da StringStream (come
	 * readString, readNumber eccetera...)
	 * <p>in caso una delle sottostringhe non possa essere convertita il metodo <strong>deve
	 * ritornare NULL</strong> 
	 * 
	 * @param stream lo stream da cui la stringa deve essere letta
	 * @return l'istanza di tipo TYPE o NULL se la stringa non sia convertibile
	 */
	public abstract TYPE readType(StringStream stream);
	
	/**contiene la lista di istruzioni per istanzializzare un array
	 * di una data lunghezza
	 * 
	 * @param length la lunghezza che deve avere l'array
	 * @return l'array di tipo TYPE
	 */
	public abstract TYPE[] getArrayInstance(int length);

}
